// （1）引入IIFE (Immidiately Invoked Function Expression 立即执行的函数表达式)
for (var i = 0; i < 5; i++) {
  ;(function (i) {
    setTimeout(function timer() {
      console.log(i)
    }, i * 1000)
  })(i)
}
// 闭包
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = (function (i) {
    return function () {
      console.log(i)
    }
  })(i)
}
// （2）利用ES 6引入的let关键字
for (let i = 0; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

for (let i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(new Date(), i)
  }, i * 1000)
}

//for 循环头部的let 声明还会有一个特殊的行为。这个行为指出变量在循环过程中不止被声明一次，每次迭代都会声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。
// （3）利用ES 5引入的bind函数
for (var i = 1; i <= 5; i++) {
  setTimeout(
    function timer(i) {
      console.log(i)
    }.bind(null, i),
    i * 1000
  )
}

for (var i = 1; i <= 5; i++) {
  setTimeout(console.log.bind(null, i), i * 1000) //null无需改变this指向, 但是i作为参数把5个i保存了起来, 作用和let一样
}

// （4）利用setTimeout第三个参数
for (var i = 1; i <= 5; i++) {
  setTimeout(
    function timer(i) {
      console.log(i)
    },
    i * 1000,
    i
  )
}
//注：setTimeout函数第三个参数及以后的参数都可以作为timer函数的参数。


// （5）把setTimeout用一个方法单独出来形成闭包,也就是在函数外调用, 和let一样是保存了5个i变量
var loop = function (i) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}
for (var i = 1; i <= 5; i++) {
  loop(i)
}

const tasks = []
for (var i = 0; i < 5; i++) {
  // 这里 i 的声明不能改成 let，如果要改该怎么做？
  ;((j) => {
    tasks.push(
      new Promise((resolve) => {
        setTimeout(() => {
          console.log(new Date(), j)
          resolve() // 这里一定要 resolve，否则代码不会按预期 work
        }, 1000 * j) // 定时器的超时时间逐步增加
      })
    )
  })(i)
}

Promise.all(tasks).then(() => {
  setTimeout(() => {
    console.log(new Date(), i)
  }, 1000) // 注意这里只需要把超时设置为 1 秒
})
